%{
#include<iostream>
#include<stdio.h>
#include <string.h>
#include"head.h"
    
extern "C"{
void yyerror(const char *s);
extern int yylex(void);
}
extern char yytext[];
extern int column;
#define YYSTYPE node*
extern int yylex();
extern FILE *yyin;
void count();
%}

%token IDENTIFIER CONSTANT STRING_LITERAL 
%token TRUE FALSE
%token INCLUDE PRINT

%token OUTPUT_OP INPUT_OP '='  
%left '@'    ITOV_OP 
%left '|'
%left RESIZE_OP CUT_OP ZOOM_OP // =>, %>, *>
%left '~'     // 1~v~1
%left LDBRACE RDBRACE 
%left AND OR
%right NOT
%left '<' '>' LE_OP GE_OP EQ_OP NE_OP
%left '+' '-'
%left '*' '/' '%'

 

%token IF  WHILE FOR RETURN TO
%nonassoc NOELSE 
%nonassoc ELSE
%token DEF PARAM_OP 
%token '[' ']' 
%token INT FLOAT VOID IMAGE VIDEO PIXEL BOOL

%start ss

%%


ss  : include_stmt_list {$$ = new start_node($1, NULL);$$->print();} 
    | include_stmt_list program 
{ $$ = new start_node($1, $2);$$ ->print();}
| program {$$ = new start_node($1,NULL);$$->print();}
      
    ;

include_stmt_list
    : include_statement
      {$$ = new list("include_stmt_list");
       $$->nodelist.push_back($1);}
    | include_stmt_list include_statement
    { $$ = $1;
    $$->nodelist.push_back($2);}
    ;

program    
    : statement 
    {$$ = new list("program");
      $$->nodelist.push_back($1);} 
    | statement program 
    {vector<node*>::iterator it=$2->nodelist.begin(); 
              $2->nodelist.insert(it,$1);
              $$=$2;}  
    ;
statement
    : compound_statement {$$ = $1;}
    | assignment_statement {$$ = $1;}      
    | if_statement {$$ = $1;}
    | for_statement {$$ = $1;} 
    | variable_declaration {$$ = $1;}
    | function_definition {$$ = $1;} 
    | function_call_statement {$$ = $1;} 
    | return_statement {$$ = $1;}  
    | print_statement {$$ = $1;}
    | input_statement {$$ = $1;} 
    | output_statement {$$ = $1;}
    ;

include_statement
    : INCLUDE STRING_LITERAL ';'   
      {$$ = new unary_stmt("include_stmt", $2);}
    ; 

print_statement
    : PRINT constant_expression ';' 
      {$$ = new unary_stmt("print_stmt", $2);}
    ; 

input_statement
    : left_value INPUT_OP STRING_LITERAL ';' 
      {$$ = new assign_node("input", $1, $3);}
    ;

output_statement
    : constant_expression OUTPUT_OP STRING_LITERAL ';'
      {$$ = new assign_node("output", $1, $3);}
    ;

variable_declaration
    : type_name var_declarator_initializer_list ';' 
      {$$ = new var_decl($1, $2);}
    ;

type_name  
    : basic_type
      {$$ = $1;}
    
          
    | type_name '[' ']' 
      {$$ = new array_func_type($1, NULL);}
    
          
    | type_name '[' constant_expression ']' 
      {$$ = new array_func_type($1, $3);}
          
    | type_name '(' ')'            
      {$$ = new array_func_type($1, NULL);}
    | type_name '(' parameter_type_declaration_list ')' 
      {$$ = new array_func_type($1, $3);}
    ;
    
basic_type
    : INT { $$ = $1;}    
    | FLOAT {$$ = $1;}
    | BOOL {$$ = $1;}
    | IMAGE {$$ = $1;}     
    | VIDEO {$$ = $1;}    
    | PIXEL {$$ = $1;}    
    | VOID  {$$ = $1;}    
    ;

constant_expression
    : constant_expression '+' constant_expression
         { node *np = new const_expr("plus", $1, $3);
           $$ = np;}
           
    | constant_expression '-' constant_expression
     { node *np = new const_expr("minus", $1, $3);
       $$ = np;}
             
    | constant_expression '*' constant_expression
     { node *np = new const_expr("multiply", $1, $3);
       $$ = np;}
           
    | constant_expression '/' constant_expression
     { node *np = new const_expr("divide", $1, $3);
       $$ = np;}
           
    | constant_expression '%' constant_expression
     { node *np = new const_expr("mod", $1, $3);
       $$ = np;}
           
    | constant_expression '>' constant_expression
      { node *np = new const_expr("greater", $1, $3);
       $$ = np;}
            
    | constant_expression '<' constant_expression
      { node *np = new const_expr("less", $1, $3);
       $$ = np;}
            
    | constant_expression LE_OP constant_expression
     { node *np = new const_expr("less_equal", $1, $3);
       $$ = np;}
          
    | constant_expression GE_OP constant_expression
     { node *np = new const_expr("greater_equal", $1, $3);
       $$ = np;}
           
    | constant_expression EQ_OP constant_expression
     { node *np = new const_expr("equal", $1, $3);
       $$ = np;}
            
    | constant_expression NE_OP constant_expression
     { node *np = new const_expr("not_equal", $1, $3);
       $$ = np;}
           
    | constant_expression AND constant_expression
     { node *np = new const_expr("and", $1, $3);
       $$ = np;}
            
    | constant_expression OR constant_expression
     { node *np = new const_expr("or", $1, $3);
       $$ = np;}
           
    | constant_expression '~' constant_expression  
     { node *np = new const_expr("fade", $1, $3);
         $$ = np;}  
       
    | constant_expression '@' constant_expression 
     { node *np = new const_expr("at", $1, $3);
       $$ = np;}  
        
    | constant_expression RESIZE_OP constant_expression  
     { node *np = new const_expr("resize", $1, $3);
       $$ = np;}
         
    | constant_expression CUT_OP constant_expression
     { node *np = new const_expr("cut", $1, $3);
       $$ = np;}
          
    | constant_expression ZOOM_OP constant_expression  
     { node *np = new const_expr("zoom", $1, $3);
       $$ = np;}
         
    | constant_expression ITOV_OP constant_expression  
     { node *np = new const_expr("itov", $1, $3);
       $$ = np;}
        
    | constant_expression LDBRACE constant_expression ':' constant_expression RDBRACE
    | unary_expression { $$ = $1;}
    ;


var_declarator_initializer_list
    : var_declarator_initializer
    {$$ = new list("var_decal_initializer_list");
    $$->nodelist.push_back($1);}
           
    | var_declarator_initializer_list ',' var_declarator_initializer 
    {$$ = $1;
    $$->nodelist.push_back($3);}
    ;

var_declarator_initializer
   : IDENTIFIER 
    {$$ = $1;}
           
    | IDENTIFIER '=' constant_expression 
     {$$ = new assign_node("equalto", $1, $3);}
           
    | IDENTIFIER INPUT_OP STRING_LITERAL 
     {$$ = new assign_node("input", $1, $3);}
    ;

unary_expression
: unary_direct_expression 
    {$$ = new unary_expr($1, NULL);}
    | unary_operator unary_direct_expression 
     {$$ = new unary_expr($1, $2);}
    ;
unary_operator
    : '-' {$$ = $1;}
    | NOT {$$ = $1;}
    ;

unary_direct_expression
    : primary_expression 
    {$$ = new unary_direct_expr($1, NULL);}
    | unary_direct_expression '[' constant_expression ']'
    {$$ = new unary_direct_expr($1, $3);}
          
    | unary_direct_expression '.' IDENTIFIER
    {$$ = new unary_direct_expr($1, $3);}
    ;

primary_expression 
: IDENTIFIER  {$$ = $1;}    
| CONSTANT  {$$ = $1;}     
| STRING_LITERAL  {$$ = $1;}
    | TRUE         
    | FALSE          
    | function_call {$$ = $1;}
    | '(' constant_expression ')' {$$ = $2;}
    | '{' constant_expression_list '}'{$$ = $2;}
    ;

constant_expression_list
         
    : constant_expression
         // $$.aaa.pushback
       {$$ = new list("const_expr_list");
             $$->nodelist.push_back($1);}
    | constant_expression_list ',' constant_expression
         // $$=S1,$$.pushback($2)
         {$$ = $1; $$->nodelist.push_back($3);}
    ;

compound_statement
    : '{' '}'  
    | '{' program '}' 
         {$$ = new unary_stmt("compound_stmt",$2);}
    ;


function_call_statement
    : function_call ';'
      {$$ = new unary_stmt("func_call_stmt", $1);}
    ;

return_statement
    : RETURN ';' 
    | RETURN constant_expression ';'
         {$$ = new unary_stmt("return_stmt", $2);}
    ;

assignment_statement
    : left_value '=' constant_expression ';'
         {$$ = new assign_node("equalto", $1, $3);}
    ;



if_statement 
    : IF constant_expression ':' statement %prec NOELSE
         {$$ = new if_stmt($2, $4);}
            
    | IF constant_expression ':' statement ELSE ':' statement 
         {$$ = new if_stmt($2, $4, $7);}
    ;


for_statement
    : FOR left_value '=' constant_expression TO constant_expression ':' statement 
         {$$ = new for_stmt($2, $4, $6, $8);}
    ;

left_value
    : unary_direct_expression {$$ = $1;}
    ;


parameter_value_list
    : parameter_direct_value_list  ',' parameter_value_assign_list
         {$$ = new list("parameter_value_list");
             $$->nodelist.push_back($1);
             $$->nodelist.push_back($3);}
         | parameter_direct_value_list {$$ = $1;}
         | parameter_value_assign_list {$$ = $1;}
    ;

parameter_direct_value_list 
    : constant_expression 
         {$$ = new list("paramter_direct_value_list");
             $$->nodelist.push_back($1);}
    | parameter_direct_value_list  ',' constant_expression
         {$$ = $1;
             $$->nodelist.push_back($3);}
    ;

parameter_value_assign_list
    : parameter_assign_value
         {$$ = new list("parameter_value_assign_list");
             $$->nodelist.push_back($1);}
    | parameter_value_assign_list ',' parameter_assign_value
         {$$ = $1;
             $$->nodelist.push_back($3);}
    ;

parameter_assign_value
    : IDENTIFIER '=' constant_expression
         {$$ = new assign_node("equalto", $1, $3);}
    ;

function_call
    : unary_direct_expression '(' ')' 
       {$$ = new func_call($1, NULL);}
    | unary_direct_expression '(' parameter_value_list ')'
         {$$ = new func_call($1, $3);}
    ;


parameter_type_declaration_list   
    : type_name 
         {$$ = new list("parameter_type_declaration_list");
             $$->nodelist.push_back($1);}
    | parameter_type_declaration_list ',' type_name
         {$$ = $1;
             $$->nodelist.push_back($3);}
    ;


parameter_declaration_list 
    : parameter_declaration
         {$$ = new list("parameter_declaration_list");
             $$->nodelist.push_back($1);
         }
    | parameter_declaration_list ',' parameter_declaration
             {$$ = $1;
              $$->nodelist.push_back($3);}
    ;

parameter_declaration
    : type_name {$$ = $1;}
    | type_name IDENTIFIER 
             {$$ = new para_declaration($1, $2, NULL);}
    | type_name IDENTIFIER '=' constant_expression
             {$$ = new para_declaration($1, $2, $4);}
    ;

function_definition 
    : DEF IDENTIFIER ':' type_name PARAM_OP '(' ')' statement
             {$$ = new func_def($2, $4, NULL, $8);}
    | DEF IDENTIFIER ':' type_name PARAM_OP '(' parameter_declaration_list ')' statement
             {$$ = new func_def($2, $4, $7, $9);}
    ;

%%
void yyerror(const char *s){
        std::cerr<<s<<std::endl;
}
int main(int argc, char**argv){
    
    ++argv, --argc; 
	if ( argc > 0 ){
        yyin = fopen( argv[0], "r" );
    }
    else{
        yyin = stdin;
    }
        yyparse();

        return 0;
}
 
